/*
 * @Date: 2022-03-02 10:32:10
 * @LastEditors: 春贰
 * @gitee: https://gitee.com/chun22222222
 * @github: https://github.com/chun222
 * @Desc:自动生成代码服务
 * @LastEditTime: 2022-10-11 09:03:42
 * @FilePath: \server\system\service\BaseService\autocode.go
 */

package BaseService

import (
	"os"
	"strings"
	"text/template"

	"go2admin_pure/system/common/sysconst"

	"go2admin_pure/system/core/db"
	"go2admin_pure/system/core/log"
	"go2admin_pure/system/model/Autocode"
	"go2admin_pure/system/model/DbModel"
	"go2admin_pure/system/model/RequestModel"
	"go2admin_pure/system/util/convert"
	"go2admin_pure/system/util/file"
	"go2admin_pure/system/util/sli"
	"go2admin_pure/system/util/str"
	utilsys "go2admin_pure/system/util/sys"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

//默认表

//前端目录 , 上级目录 + web
var FrontWebDir = str.BeforeLast(strings.Replace(utilsys.ExecutePath(), "/", "\\", -1), "\\") + "\\web\\"

var GenerateAll = [...]Autocode.GenerateModel{
	{TplFile: "system/template/back/modeldb.tpl", TargetDir: "app/model/DbModel/", Type: "DbModel"},
	{TplFile: "system/template/back/modelrequest.tpl", TargetDir: "app/model/RequestModel/", Type: "RequestModel"},
	{TplFile: "system/template/back/router.tpl", TargetDir: "app/router/", Type: "router"},
	{TplFile: "system/template/back/service.tpl", TargetDir: "app/service/", Type: "service"},
	{TplFile: "system/template/back/controller.tpl", TargetDir: "app/controller/", Type: "controller"},
	{TplFile: "system/template/front/index.tpl", TargetDir: "src/app/view/*/index.vue", Type: "front"},
	{TplFile: "system/template/front/add.tpl", TargetDir: "src/app/view/*/modal/add.vue", Type: "front"},
	{TplFile: "system/template/front/edit.tpl", TargetDir: "src/app/view/*/modal/edit.vue", Type: "front"},
	{TplFile: "system/template/front/info.tpl", TargetDir: "src/app/view/*/modal/info.vue", Type: "front"},
	{TplFile: "system/template/front/router.tpl", TargetDir: "src/app/route/*.js", Type: "front"},
	{TplFile: "system/template/front/api.tpl", TargetDir: "src/app/api/*.js", Type: "front"},
}

type AutocodeService struct{}

func (AutocodeService *AutocodeService) Create(r RequestModel.CodeCreate) (err error) {

	//生成替换数据
	var tar Autocode.TargetData
	tar.TableName = strings.ToLower(r.TableName)
	tar.StructName = str.Case2Camel(r.TableName)
	for _, v := range r.Tabledata {
		tar.Fields = append(tar.Fields, AutocodeService.typedic(v, &tar))
		if v.Search {
			tar.SearchFields = append(tar.SearchFields, AutocodeService.typedic(v, &tar))
		}
	}
	//生成权限
	if err := AutocodeService.CreatePermissions(r); err != nil {
		return err
	}

	//数据模型
	basedir := utilsys.ExecutePath() + "\\" //根目录

	for _, v := range GenerateAll {
		if v.Type == "controller" {
			file.MkDir(basedir + v.TargetDir + tar.TableName)
			AutocodeService.generate(v.TplFile, v.TargetDir+tar.TableName+"/"+r.TableName+".go", tar)
		} else if v.Type == "front" {
			realPath := strings.Replace(v.TargetDir, "*", tar.TableName, -1)
			realDir := str.BeforeLast(realPath, "/")
			file.MkDir(FrontWebDir + realDir)
			AutocodeService.generate(v.TplFile, FrontWebDir+realPath, tar)
		} else {
			file.MkDir(basedir + v.TargetDir)
			AutocodeService.generate(v.TplFile, v.TargetDir+r.TableName+".go", tar)
		}
	}

	//特殊处理InitRouter
	tar.Routers = file.GetDirfileNames("app/router/")
	tar.Dbtables = file.GetDirfileNames("app/model/DbModel/")
	for i, v := range tar.Dbtables {
		tar.Dbtables[i] = str.Case2Camel(v)
	}
	AutocodeService.generate("system/template/back/Init.tpl", "app/Init.go", tar)

	return nil
}

//删除
func (AutocodeService *AutocodeService) Remove(TableName string) (err error) {

	db.Instance().Migrator().DropTable(TableName) //删除表
	for _, v := range GenerateAll {
		if v.Type == "controller" {
			os.Remove(v.TargetDir + TableName + "/" + TableName + ".go")
		} else if v.Type == "front" {
			realDir := strings.Replace(v.TargetDir, "*", TableName, -1)
			os.Remove(FrontWebDir + realDir)
		} else {
			os.Remove(v.TargetDir + TableName + ".go")
		}
	}
	//特殊处理InitRouter
	var tar Autocode.TargetData
	tar.Routers = file.GetDirfileNames("app/router/")
	tar.Dbtables = file.GetDirfileNames("app/model/DbModel/")
	for i, v := range tar.Dbtables {
		tar.Dbtables[i] = str.Case2Camel(v)
	}
	AutocodeService.generate("system/template/back/Init.tpl", "app/Init.go", tar)
	return nil
}

//创建权限
func (AutocodeService *AutocodeService) CreatePermissions(r RequestModel.CodeCreate) error {

	return db.Instance().Transaction(func(tx *gorm.DB) error {

		data := DbModel.SysPermission{Parent: uint(r.Parent), Title: r.Ename, Name: "/" + strings.ToLower(r.TableName) + "/list", Type: "menu", Status: 1}
		// 使用 'tx' 而不是 'db'
		if err := tx.Clauses(clause.OnConflict{
			UpdateAll: true,
		}).Create(&data).Error; err != nil {
			// 返回任何错误都会回滚事务
			return err
		}

		var datas []DbModel.SysPermission
		datas = append(datas, DbModel.SysPermission{Parent: data.ID, Title: "add " + r.Ename, Name: "/" + strings.ToLower(r.TableName) + "/add", Type: "button", Status: 1},
			DbModel.SysPermission{Parent: data.ID, Title: "edit " + r.Ename, Name: "/" + strings.ToLower(r.TableName) + "/edit", Type: "button", Status: 1},
			DbModel.SysPermission{Parent: data.ID, Title: "remove " + r.Ename, Name: "/" + strings.ToLower(r.TableName) + "/remove", Type: "button", Status: 1})
		// 使用 'tx' 而不是 'db'
		if err := tx.Clauses(clause.OnConflict{
			UpdateAll: true,
		}).Create(&datas).Error; err != nil {
			// 返回任何错误都会回滚事务
			return err
		}
		// 返回 nil 提交事务
		return nil
	})

}

//获取所有表
func (AutocodeService *AutocodeService) TableList() []Autocode.TableModel {
	var tablelist []Autocode.TableModel
	// db.Instance().Raw("select table_name from information_schema.tables where table_schema=?", db.Instance().Migrator().CurrentDatabase()).Scan(&tablelist)
	tables, _ := db.Instance().Migrator().GetTables()
	for _, v := range tables {
		if !sli.InSlice(v, sysconst.SysTables) {

			tablelist = append(tablelist, Autocode.TableModel{
				TableName: v,
			})

		}
	}
	return tablelist

}

//查询字段
func (AutocodeService *AutocodeService) TableFields(table_name string) []Autocode.ColumnModel {

	var columnList []Autocode.ColumnModel
	db.Instance().Raw("SELECT column_name,column_comment,is_nullable,column_type FROM information_schema.columns WHERE table_schema =? AND table_name = ?", db.Instance().Migrator().CurrentDatabase(), table_name).Scan(&columnList)

	return columnList
	//db.Migrator().CurrentDatabase()
}

//查询权限目录
func (AutocodeService *AutocodeService) PermissionDir() []DbModel.SysPermission {

	var dirlist []DbModel.SysPermission
	db.Instance().Where("type = ?", "dir").Order("sort").Find(&dirlist)
	return dirlist
}

//模板生成
func (AutocodeService *AutocodeService) generate(tplFile string, targetFile string, data interface{}) error {

	//存在文件
	if !file.CheckNotExist(targetFile) && targetFile != "app/Init.go" {
		log.Write(log.Error, "代码生成"+targetFile+"文件失败，目标文件存在！")
		return nil
	}

	tmpl, err := template.ParseFiles(tplFile)
	os.Remove(targetFile)
	file, err := os.OpenFile(targetFile, os.O_CREATE|os.O_WRONLY, 0o755)
	if err != nil {
		return err
	}
	if err = tmpl.Execute(file, data); err != nil {
		return err
	}
	_ = file.Close()
	return nil
}

//类型转换,生成数据
func (AutocodeService *AutocodeService) typedic(r RequestModel.Tabledata, h *Autocode.TargetData) Autocode.TargetFields {
	var tar Autocode.TargetFields
	tar.Name = str.Case2Camel(r.Fieldname)
	tar.Type = "string"
	tar.Comment = r.Comment
	tar.Len = r.Len
	tar.List = r.List
	if r.Notnull {
		tar.Binding = "required"
	} else {
		tar.Binding = "-"
	}
	tar.Json = strings.ToLower(r.Fieldname)

	if r.Type == "time" {
		tar.Type = "time.Time"
		h.DbImportString = `import ( "time" )`
		if r.Search {
			h.ReImportString = `import ("time")`
		}
	} else {
		tar.Type = r.Type
	}

	//Search
	tar.Search = r.Search
	tar.Notnull = r.Notnull
	tar.InputType = r.InputType
	//Gorm
	gorm_size := ""
	gorm_null := ""
	if r.Type == "string" {
		gorm_size = "size:" + convert.String(r.Len) + ";"
	}
	if r.Notnull {
		gorm_null = "not null;"
	}
	if strings.ToLower(r.Fieldname) == "id" {
		tar.Gorm = "primaryKey;autoIncrement;comment:id"
	} else {
		tar.Gorm = gorm_size + gorm_null + "comment:" + r.Comment
	}
	return tar
}
